💡 AI 인사이트

🤖 AI가 여기에 결과를 출력합니다...

댓글 커뮤니티

쿠팡이벤트

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

검색

    로딩 중이에요... 🐣

    [코담] 웹개발·실전 프로젝트·AI까지, 파이썬·장고의 모든것을 담아낸 강의와 개발 노트

    23 좋아요 기능 백엔드 API 구현 | ✅ 편저: 코담 운영자

    23강 - 좋아요 기능: 백엔드 API 구현

    기능 - API


    ✨ 이번 강의 목표

    • Django 모델에서 좋아요 기능을 위한 필드 구성
    • Django REST Framework(GenericAPIView)를 사용한 API 구현
    • 좋아요 추가 및 취소 기능 구현 (POST 요청)

    1. 모델 설정

    django_instagram/posts/models.py

    # 게시글
    class Post(TimeStampedModel):
        author = models.ForeignKey(
            user_models.User,
            null=True,  # DB에서 null 허용
            on_delete=models.CASCADE,
            related_name='post_author',
            verbose_name=_("작성자")
        )
        image = models.ImageField(_("이미지"), upload_to="posts/", blank=False)
        caption = models.TextField(_("내용"), blank=False)
        image_likes = models.ManyToManyField(
            user_models.User,
            blank=True,
            related_name='post_image_likes',
            verbose_name=_("좋아요")
        )
    
        def __str__(self):
            return f"{self.author} : {self.caption}"
    
        class Meta:
            verbose_name = _("게시물")
            verbose_name_plural = _("게시물들")
            ordering = ['-created_at']
    
    • image_likes: 게시글에 좋아요를 누른 유저 목록을 저장하는 M2M 필드
    • __str__: admin 등에서 객체 표시를 명확히 하기 위해 사용

    2. URL 패턴

    django_instagram/posts/api/urls.py

    from django.urls import path
    from .views import PostListView, PostLikeView
    from . import views
    
    app_name = "posts_api"
    
    urlpatterns = [
        path("posts/", PostListView.as_view(), name="posts-list"),
        path("posts/list", views.posts_list_view, name="posts-list"),
        path("posts/<int:post_id>/post_like/", PostLikeView.as_view(), name="post_like"),
    ]
    

    3. DRF 기반 좋아요 API View

    django_instagram/posts/api/views.py

    from django.shortcuts import get_object_or_404
    from django_instagram.users.models import User as user_model
    from rest_framework.response import Response
    from rest_framework import status, generics, permissions
    from django_instagram.posts.models import Post
    from .serializers import PostSerializer, CommentFormSerializer, UserSerializer
    from django.db.models import Q
    from django_instagram.posts.forms import CommentForm
    from django_instagram.posts import models
    from django.http import JsonResponse
    from rest_framework.decorators import api_view
    from django.contrib.auth.decorators import login_required
    from django.views.decorators.http import require_POST
    
    # DRF 방식: 게시글 리스트
    class PostListView(generics.ListAPIView):
        serializer_class = PostSerializer
        permission_classes = [permissions.IsAuthenticated]
    
        def get_queryset(self):
            user = get_object_or_404(user_model, pk=self.request.user.id)
            following = user.following.all()
            return Post.objects.filter(Q(author__in=following) | Q(author=user)).order_by('-created_at')
    
        def list(self, request, *args, **kwargs):
            queryset = self.get_queryset()
            serializer = self.get_serializer(queryset, many=True, context={'request': request})
            login_user_serializer = UserSerializer(request.user, context={'request': request})
            return Response({"posts": serializer.data, "loginUser": login_user_serializer.data}, status=status.HTTP_200_OK)
    
    # FBV 방식: 게시글 리스트
    @api_view(['GET'])
    def posts_list_view(request):
        if request.method == 'GET':
            user = get_object_or_404(user_model, pk=request.user.id)
            following = user.following.all()
            followed_posts = models.Post.objects.filter(Q(author__in=following) | Q(author=user)).order_by('-created_at')
            serializer = PostSerializer(followed_posts, many=True, context={'request': request})
            login_user_serializer = UserSerializer(request.user, context={'request': request})
            return JsonResponse({"posts": serializer.data, "loginUser": login_user_serializer.data}, status=200)
    
    
    
    # DRF: 좋아요 추가 및 취소
    class PostLikeView(generics.GenericAPIView):
        permission_classes = [permissions.IsAuthenticated]
    
        def post(self, request, post_id):
            post = get_object_or_404(models.Post, pk=post_id)
            user = request.user
    
            if post.image_likes.filter(pk=user.pk).exists():
                post.image_likes.remove(user)
                is_added = False
            else:
                post.image_likes.add(user)
                is_added = True
    
            return Response({
                "success": True,
                "message": "like" if is_added else "dislike",
                "like_count": post.image_likes.count()
            }, status=status.HTTP_200_OK)
    

    ✅ 주요 설명

    • GenericAPIView를 상속받아 POST 메서드만 오버라이드
    • request.user가 이미 좋아요를 눌렀는지 확인 후 add/remove 처리
    • 응답으로 성공 여부, 상태 메시지, 좋아요 수 반환

    4. FBV 방식의 대안 구현 (선택사항)

    # FBV: 좋아요 추가 및 취소
    @require_POST
    @login_required
    def post_like(request, post_id):
        post = get_object_or_404(models.Post, pk=post_id)
    
        try:
            if post.image_likes.filter(pk=request.user.pk).exists():
                post.image_likes.remove(request.user)
                is_added = False
            else:
                post.image_likes.add(request.user)
                is_added = True
    
            return JsonResponse({
                "success": True,
                "message": "like" if is_added else "dislike",
                "like_count": post.image_likes.count()
            }, status=200)
    
        except Exception as e:
            return JsonResponse({"success": False, "message": "오류가 발생했습니다.", "error": str(e)}, status=500)
    
    • FBV(Function Based View) 방식에서도 동일한 로직 구현 가능
    • Django 데코레이터 기반 (@login_required, @require_POST)

    ✅ 정리

    항목 설명
    모델 필드 image_likes (ManyToManyField)로 좋아요 구현
    API View PostLikeView: POST 요청으로 add/remove 처리
    응답 구조 { success, message, like_count }
    추가 설명 DRF 또는 FBV 중 하나 선택해 구현 가능

    👉 다음 강의에서는 Ajax 호출 시 CSRF 토큰과 함께 이 API를 연동합니다.

    TOP
    preload preload